package org.artifact.core.plugin.netty.server;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.TimeUnit;

/**
 * @author : SandKing
 * @CreateDate : 2020/3/25 15:42
 * @Description ：Please describe this document
 */
public class NettyTcpServer implements NettyServer{

    static Log log = LogFactory.get();

    private ServerBootstrap bootstrap;
    /** Boss线程就用一个线程 */
    private EventLoopGroup bossGroup;
    /** Work线程:CPU<=4的话CPU*2,CPU<=8的话CPU+4, 其他直接使用12 */
    private final EventLoopGroup workGroup;

    private boolean epollActive = false;

    private int nThreads = 0;

    /** Netty低水位，默认值32K */
    private int defaultLowWaterMark = 32 * 1024;
    /** Netty高水位，默认值64K */
    private int defaultHighWaterMark = 64 * 1024;

    private int heartbeat = 1000;

    private int port;

    public NettyTcpServer() {
        if (epollActive && Epoll.isAvailable()) {
            this.bossGroup = new EpollEventLoopGroup(1);
            this.workGroup = new EpollEventLoopGroup(nThreads);
            bootstrap.group(bossGroup, workGroup).channel(EpollServerSocketChannel.class);
        } else {
            this.bossGroup = new NioEventLoopGroup(1);
            this.workGroup = new NioEventLoopGroup(nThreads);
            bootstrap.group(bossGroup, workGroup).channel(NioServerSocketChannel.class);
        }

        // Socket参数，地址复用，默认值false
        bootstrap.option(ChannelOption.SO_REUSEADDR, true);
        // Socket参数，服务端接受连接的队列长度，如果队列已满，客户端连接将被拒绝。默认值，Windows为200，其他为128。
        bootstrap.option(ChannelOption.SO_BACKLOG, 65535);

        // TCP参数，立即发送数据，默认值为Ture（Netty默认为True而操作系统默认为False）。
        // 该值设置Nagle算法的启用，改算法将小的碎片数据连接成更大的报文来最小化所发送的报文的数量，
        // 如果需要发送一些较小的报文，则需要禁用该算法。Netty默认禁用该算法，从而最小化报文传输延时。
        bootstrap.childOption(ChannelOption.TCP_NODELAY, true);

        // Netty参数，写低水位标记，默认值32KB。当Netty的写缓冲区中的字节超过高水位之后若下降到低水位，则Channel的isWritable()返回True。
        // Netty参数，写高水位标记，默认值64KB。如果Netty的写缓冲区中的字节超过该值，Channel的isWritable()返回False。
        bootstrap.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(defaultLowWaterMark, defaultHighWaterMark));

        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) {
                buildChannelPipeline(ch.pipeline());
            }
        });
    }

    /**
     * 构建ChannelPipeline通道.
     *
     * @param pipeline ChannelPipeline通道
     */
    protected void buildChannelPipeline(ChannelPipeline pipeline) {
        // 配置了心跳功能，则启用心跳机制.
        if (heartbeat > 0) {
            pipeline.addLast("idleStateHandler", new IdleStateHandler(heartbeat, 0, 0, TimeUnit.SECONDS));
        }

        // 输出具体的Netty接受与发送封包的日志
//        if (logActive) {
//            pipeline.addLast("logger", new LoggingHandler(LogLevel.DEBUG));
//        }

        // 统一默认的事件处理...
//        pipeline.addLast(nettyServerHandler);

        // 初始化封包处理器.
//        pipeline.addLast(new InitializeDecoder(initializeHandlerManager));
    }


    @Override
    public void startup() {
        log.info("game tcp server start on {}", port);

        // 如果封包日志打开的话，需要桥接进Noark日志实现
//        if (logActive) {
//            InternalLoggerFactory.setDefaultFactory(NettyLoggerFactory.INSTANCE);
//        }

        try {
            bootstrap.bind(port).sync();
            log.info("game tcp server start is success.");
        } catch (Exception e) {
            log.error(e);
        }
    }

    @Override
    public void shutdown() {
        try {
            bossGroup.shutdownGracefully().sync();
            workGroup.shutdownGracefully().sync();
            log.info("Netty Server Stop Success!");
        } catch (InterruptedException e) {
            log.error(e);
        }
    }
}
